/*
 * =====================================================================================
 *
 *       Filename:  test_server.cxx
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  03/25/2011 02:46:54 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Ken Chen (Ken), chen_kenz@emc.com
 *        Company:  EMC
 *
 * =====================================================================================
 */

#ifndef __GXX_EXPERIMENTAL_CXX0X__
#define __GXX_EXPERIMENTAL_CXX0X__
#endif

#include <signal.h>
#include <cstdlib>
#include <cassert>
#include <functional>
#include <iostream>
#include "cl_singleton.hxx"
#include "cl_signal_handler.hxx"
#include "cl_tcp_server.hxx"
#include "cl_tcp_conn.hxx"
#include "cl_buffer.hxx"
#include "cl_timestamp.hxx"

using namespace std;
using namespace std::placeholders;
using namespace neo;

static void handlePIPE(void)
{
  cerr << "PIPE caught\n";
}

class EchoServer
{
public:
  EchoServer()
    : m_tcpServer("localhost", 1234)
  {
    m_tcpServer.setThreadNum(0);
    m_tcpServer.setConnCallback(bind(&EchoServer::handleConnection, this, _1));
	  m_tcpServer.setMessageCallback(bind(&EchoServer::handleMsg, this, _1, _2, _3));
  }

  void start(void)
  {
    m_tcpServer.start();
  }

private:
  void handleConnection(const CLTcpConnSPtr& conn)
  {
    cout << "caught a new connection\n";
  }

  void handleMsg(const CLTcpConnSPtr& conn, CLBuffer& buf, const CLTimestamp& tim)
  {
    string msg;
    buf.read(msg); 
    cout << "receive msg: " << msg << " at " << tim.toFormatString() << "\n";
    conn->send(msg);
  }

private:
  CLTcpServer m_tcpServer;
};

class DiscardServer
{
public:
  DiscardServer()
    : m_tcpServer("localhost", 1235)
  {
    m_tcpServer.setThreadNum(0);
    m_tcpServer.setConnCallback(bind(&DiscardServer::handleConnection, this, _1));
	  m_tcpServer.setMessageCallback(bind(&DiscardServer::handleMsg, this, _1, _2, _3));
  }

  void start(void)
  {
    m_tcpServer.start();
  }

private:
  void handleConnection(const CLTcpConnSPtr& conn)
  {
    cout << "caught a new connection\n";
  }

  void handleMsg(const CLTcpConnSPtr& conn, CLBuffer& buf, const CLTimestamp& tim)
  {
    string msg;
    buf.read(msg); 
    cout << "discards msg: " << msg << " at " << tim.toFormatString() << "\n";
  }

private:
  CLTcpServer m_tcpServer;
};

class DaytimeServer
{
public:
  DaytimeServer()
    : m_tcpServer("localhost", 1236)
  {
    m_tcpServer.setThreadNum(0);
    m_tcpServer.setConnCallback(bind(&DaytimeServer::handleConnection, this, _1));
  }
  
  void start(void)
  {
    m_tcpServer.start();
  }

private:
  void handleConnection(const CLTcpConnSPtr& conn)
  {
    conn->send(CLTimestamp::nowTime().toFormatString());
    conn->shutDown();
  }

private:
  CLTcpServer m_tcpServer;
};

class ChargenServer
{
public:
  ChargenServer()
    : m_tcpServer("localhost", 1237),
    m_startTime(CLTimestamp::nowTime()),
    m_transferred(0)
  {
    m_tcpServer.setThreadNum(0);
    m_tcpServer.setConnCallback(bind(&ChargenServer::handleConnection, this, _1));
    m_tcpServer.setWriteCompleteCallback(bind(&ChargenServer::handleWriteComplete, this, _1));
    m_tcpServer.setMessageCallback(bind(&ChargenServer::handleMsg, this, _1, _2, _3));

    string lin;
    for(int i = 33; i < 127; ++i)
    {
      lin.push_back(char(i));
    }
    lin += lin;

    for(int j = 0; j < 127 - 33; ++j)
    {
      m_msg += lin.substr(j, 72) + '\n';
    }
  }

  void start(void)
  {
    m_tcpServer.start();
  }

private:
  void handleConnection(const CLTcpConnSPtr& conn)
  {
    conn->send(m_msg);
  }

  void handleMsg(const CLTcpConnSPtr& conn, CLBuffer& buf, const CLTimestamp& tim)
  {
    string msg;
    buf.read(msg);
    cout << "Discards: " << msg << "\n";
  }

  void handleWriteComplete(const CLTcpConnSPtr& conn)
  {
    m_transferred += m_msg.size();
    conn->send(m_msg);
  }

private:
  CLTcpServer m_tcpServer;
  CLTimestamp m_startTime;
  string m_msg;
  int64_t m_transferred;
};

int main(int argc, char* argv[])
{
  CLSignalHandler sigHandler;
  sigHandler.installHandlerFor(SIGPIPE, bind(handlePIPE));

  EchoServer echoServer;
  echoServer.start();

  DiscardServer discardServer;
  discardServer.start();

  DaytimeServer daytimeServer;
  daytimeServer.start();

  ChargenServer chargenServer;
  chargenServer.start();

  sigHandler.waitForSignal();
}

